home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 11 - 1995 / 11.04 Apr 95 / TreeAppƒ / Eric's C++ Libraries / Interface Classes / CPPVisualTreeNode.cp < prev    next >
Encoding:
Text File  |  1996-04-04  |  29.6 KB  |  987 lines  |  [TEXT/KAHL]

  1. /***************************************************** IMPLEMENTATION
  2.     DATE:    10/28/93
  3.     AUTHOR: Eric R. Rosé
  4.  
  5.     CLASS:  CPPVisualTreeNode
  6.     
  7.     SUPERCLASS: CPPTreeNode
  8.     
  9.         This C++ class manages the visual representation of a node
  10.         in a hierarchical tree
  11.     
  12. ********************************************************************/
  13.  
  14. #include <CPPVisualTreeNode.h>
  15. #include <CPPVisualTree.h>
  16. #include "CPPString.h"
  17. #include <MathTools.h>
  18. #include <StringTools.h>
  19. #include <ConvertTools.h>
  20. #include <ToolboxTools.h>
  21. #include <math.h>
  22.  
  23.  
  24. #define    hMargin    2
  25. #define    vMargin    2
  26.  
  27. /*-----------------------------------------------------------------*/
  28. /*------------------------ APPLY ROUTINES -------------------------*/
  29. /*-----------------------------------------------------------------*/
  30.  
  31.     void    SetMustResize (CPPTreeNode *theNode, long param)
  32.     /* set the value of 'needsResize' to the passed-in value */
  33.     {
  34.         if (theNode)
  35.           {
  36.             ((CPPVisualTreeNode *)theNode)->needsResize = (Boolean)param;
  37.             ((CPPVisualTreeNode *)theNode)->needsMove = (Boolean)param;
  38.           }
  39.     }
  40.  
  41.     void    SetMustMove (CPPTreeNode *theNode, long param)
  42.     /* set the value of 'needsMove' to the passed-in value */
  43.     {
  44.         if (theNode)
  45.           ((CPPVisualTreeNode *)theNode)->needsMove = (Boolean)param;
  46.     }
  47.  
  48.     void    SetNotYetPlaced (CPPTreeNode *theNode, long param)
  49.     /* set the value of 'needsDraw' to the passed-in value */
  50.     {
  51.         if (theNode)
  52.           ((CPPVisualTreeNode *)theNode)->notYetPlaced = (Boolean)param;
  53.     }
  54.     
  55.     void    SetMustDraw (CPPTreeNode *theNode, long param)
  56.     /* set the value of 'needsDraw' to the passed-in value */
  57.     {
  58.         if (theNode)
  59.           ((CPPVisualTreeNode *)theNode)->needsDraw = (Boolean)param;
  60.     }
  61.     
  62.     void    CountSelected (CPPTreeNode *theNode, long param)
  63.     /* if the node is selected, increment param */
  64.     {
  65.         if (((CPPVisualTreeNode *)theNode)->IsSelected())
  66.           (*((long *)param))++;
  67.     }
  68.     
  69. /*-----------------------------------------------------------------*/
  70. /*------------------------ PUBLIC METHODS -------------------------*/
  71. /*-----------------------------------------------------------------*/
  72.  
  73.     CPPVisualTreeNode::CPPVisualTreeNode (CPPObject *NodeData, 
  74.                                           CPPTree *BelongsTo, 
  75.                                           Boolean becomeOwner,
  76.                                           Boolean selected) :
  77.                        CPPTreeNode (NodeData, BelongsTo, becomeOwner)
  78.     {
  79.         SetPt (&this->nodeSize, 0, 0);
  80.         this->familySize = this->childSize = this->nodeSize;
  81.         
  82.         SetRect (&this->nodeRect, 0, 0, 0, 0);
  83.         this->familyRect = this->childRect = this->nodeRect;
  84.                 
  85.         this->needsDraw = TRUE;
  86.         this->needsResize = TRUE;
  87.         this->needsMove = TRUE;
  88.         this->isSelected = selected;
  89.         this->notYetPlaced = TRUE;
  90.     }
  91.  
  92. /*-----------------------------------------------------------------*/
  93.  
  94.     CPPVisualTreeNode::~CPPVisualTreeNode (void)
  95.     {
  96.  
  97.     }
  98.             
  99. /*-----------------------------------------------------------------*/
  100.  
  101.     Boolean    CPPVisualTreeNode::Member (char *className)
  102.     {
  103.         if (strcmp(className, CPPVisualTreeNode::ClassName()) == 0)
  104.           return TRUE;
  105.         else
  106.           return CPPTreeNode::Member(className);
  107.     }
  108.  
  109. /*-----------------------------------------------------------------*/
  110.  
  111.     char     *CPPVisualTreeNode::ClassName (void)
  112.     {
  113.         return "CPPVisualTreeNode";
  114.     }
  115.  
  116. /*-----------------------------------------------------------------*/
  117.  
  118.     CPPObject *CPPVisualTreeNode::Clone(void)
  119.     {
  120.         return NULL;
  121.     }
  122.     
  123. /*-----------------------------------------------------------------*/
  124.  
  125.     void    CPPVisualTreeNode::DrawNodeData (void)
  126.     /* This method draws the specific data for the node */
  127.     /* SUBCLASS SHOULD OVERRIDE */
  128.     {
  129.         StringPtr    STemp;
  130.         Rect        tempRect = this->nodeRect;
  131.  
  132.         /*•*/        
  133.         if (this->nodeData)
  134.           {
  135.               STemp = ((CPPString *)this->nodeData)->GetString(TRUE);
  136.               if (STemp)
  137.                 {
  138.                     PStrReplace (STemp, ' ', '\r');
  139.                   TextBox (STemp+1, *STemp, &tempRect, teJustCenter);
  140.                   DisposePtr((Ptr)STemp);
  141.                 }
  142.           }
  143.         /*•*/
  144.         
  145.         FrameRect (&tempRect);
  146.         if (this->IsSelected())
  147.           {
  148.               InsetRect(&tempRect, 1, 1);
  149.               InvertRect(&tempRect);
  150.           }
  151.     }
  152.  
  153. /*-----------------------------------------------------------------*/
  154.  
  155.     Boolean    CPPVisualTreeNode::CanDraw (void)
  156.     /* Return TRUE either if this node needs to redraw, or the */
  157.     /* tree's ForceDraw variable is set to TRUE */
  158.     {
  159.         if (this->needsDraw)
  160.           return TRUE;
  161.         else
  162.           {
  163.             if ((this->Root) && ((CPPVisualTree *)this->Root)->forceDraw)
  164.               return TRUE;
  165.           }
  166.         return FALSE;
  167.     }
  168.  
  169. /*-----------------------------------------------------------------*/
  170.  
  171.     void    CPPVisualTreeNode::DrawNode (Boolean wholeFamily,
  172.                                          Boolean forceDraw)
  173.     {
  174.         CPPVisualTreeNode *theChild;
  175.         
  176.         if (!CanDraw() && !forceDraw && !wholeFamily) return;
  177.         
  178.         // prepare the grafport to draw tree nodes
  179.         if (this->Root)
  180.           ((CPPVisualTree *)this->Root)->Prepare(this);
  181.         
  182.         if (CanDraw() || forceDraw)
  183.           {
  184.             // draw the join between this node and its parent
  185.             if (this->Parent)
  186.               DrawJoin ((CPPVisualTreeNode *)this->Parent, this);
  187.             
  188.             // draw the data in the node;
  189.             DrawNodeData();
  190.             
  191.             this->needsDraw = FALSE;
  192.           }
  193.         
  194.         // draw the node's children
  195.         if (wholeFamily)
  196.           {
  197.             for (long i = 1; i <= this->numItems; i++)
  198.               {
  199.                 theChild = (CPPVisualTreeNode *)NthChild (i);
  200.                 if (theChild)
  201.                   theChild->DrawNode(wholeFamily, forceDraw);
  202.               }
  203.           }
  204.         
  205.         // restore the original grafport state
  206.         ((CPPVisualTree *)this->Root)->Restore(this);
  207.     }
  208.     
  209. /*-----------------------------------------------------------------*/
  210.  
  211.     void    CPPVisualTreeNode::EraseJoin (CPPVisualTreeNode *FromNode,
  212.                                          CPPVisualTreeNode *ToNode)
  213.     /* erase the join between the two passed-in nodes. This method */
  214.     /* simply uses the Tree's default join method which takes into */
  215.     /* account the orientation and join type of the tree */
  216.     {
  217.         PenState    OldState;
  218.         
  219.         GetPenState (&OldState);
  220.         PenMode (patBic);
  221.         DrawJoin (FromNode, ToNode);
  222.         SetPenState (&OldState);
  223.     }
  224.  
  225. /*-----------------------------------------------------------------*/
  226.  
  227.     void    CPPVisualTreeNode::DrawJoin (CPPVisualTreeNode *FromNode,
  228.                                          CPPVisualTreeNode *ToNode)
  229.     /* Draw the join between the two passed-in nodes. This method */
  230.     /* simply uses the Tree's default join method which takes into */
  231.     /* account the orientation and join type of the tree */
  232.     {
  233.         if ((FromNode && !FromNode->notYetPlaced) && 
  234.             (ToNode && !ToNode->notYetPlaced) && 
  235.             this->Root)
  236.           ((CPPVisualTree *)this->Root)->DrawDefaultJoin (FromNode, ToNode);
  237.     }
  238.  
  239. /*-----------------------------------------------------------------*/
  240.  
  241.     void    CPPVisualTreeNode::EraseNode (Boolean wholeFamily)
  242.     {
  243.         PenState    OldState;
  244.         
  245.         // prepare the grafport to draw tree nodes
  246.         if (this->Root)
  247.           ((CPPVisualTree *)this->Root)->Prepare(this);
  248.         
  249.         if (wholeFamily)
  250.           {
  251.             EraseRect (&this->familyRect);
  252.             ApplyToFamily (this, SetMustDraw, (long)TRUE);
  253.           }
  254.         else
  255.           EraseRect (&this->nodeRect);
  256.  
  257.         EraseJoin((CPPVisualTreeNode *)this->Parent, this);
  258.  
  259.         // restore the original grafport state
  260.         ((CPPVisualTree *)this->Root)->Restore(this);
  261.     }
  262.     
  263. /*-----------------------------------------------------------------*/
  264.  
  265.     void    CPPVisualTreeNode::CalcFamilyBounds (Point TopLeftCorner)
  266.     /* Given the top left corner of the tree, call DoCalcFamilyBounds */
  267.     /* with the apropriate parameter depending on the orientation of */
  268.     /* the tree */
  269.     {
  270.         short    deltaH = TopLeftCorner.h - this->familyRect.left,
  271.                 deltaV = TopLeftCorner.v - this->familyRect.top;
  272.         Point    TopRight = {this->familyRect.top + deltaV, this->familyRect.right + deltaH},
  273.                 BottomLeft = {this->familyRect.bottom + deltaV, this->familyRect.left + deltaH};
  274.     
  275.         if (!this->needsMove) return;
  276.         this->needsMove = FALSE;
  277.                                 
  278.         // then figure out where everybody goes
  279.         switch (GetOrientation()) {
  280.             case kTopDown:
  281.             case kLeft2Right :
  282.                 DoCalcFamilyBounds (TopLeftCorner);
  283.                 break;
  284.             case kBottomUp:
  285.                 DoCalcFamilyBounds (BottomLeft);
  286.                 break;
  287.             case kRight2Left:
  288.                 DoCalcFamilyBounds (TopRight);
  289.                 break;
  290.         }
  291.     }
  292.     
  293. /*-----------------------------------------------------------------*/
  294.  
  295.     void    CPPVisualTreeNode::CalcFamilySize (void)
  296.     {
  297.         DoCalcFamilySize();
  298.     }
  299.  
  300. /*-----------------------------------------------------------------*/
  301.  
  302.     void    CPPVisualTreeNode::ResizeFamily (Boolean wholeFamily)
  303.     /* set the 'must resize' flags of the node (and maybe its family) */
  304.     /* then make the call to resize and calculate the new boundaries */
  305.     /* of each node */
  306.     {
  307.         Point    oldTopLeft = {this->familyRect.top, this->familyRect.left};
  308.         
  309.         if (wholeFamily)
  310.           ApplyToFamily (this, SetMustResize, (long)TRUE);
  311.         this->needsResize = TRUE;
  312.         this->needsMove = TRUE;
  313.         
  314.         // first figure out how big everyone is
  315.         CalcFamilySize();
  316.         
  317.         // then figure out where everybody goes
  318.         CalcFamilyBounds(oldTopLeft);
  319.     }
  320.     
  321. /*-----------------------------------------------------------------*/
  322.  
  323.     void    CPPVisualTreeNode::MoveFamily (Point *topLeft)
  324.     /* move the family over to the specified coordinates */
  325.     {        
  326.         Point    oldTopLeft = {this->familyRect.top, this->familyRect.left};
  327.                 
  328.         if (!EqualPt(*topLeft, oldTopLeft))
  329.           {
  330.               ApplyToFamily(this, SetMustMove, (long)TRUE);
  331.               CalcFamilyBounds(*topLeft);
  332.           }
  333.     }
  334.     
  335. /*-----------------------------------------------------------------*/
  336.         
  337.     void CPPVisualTreeNode::CalcNodeSize()
  338.     /* Calculate how large our node's data is and store in nodeSize */
  339.     /* SUBCLASS SHOULD OVERRIDE */
  340.     {
  341.         /*•*/        
  342.         FontInfo    fInfo;
  343.         StringPtr    STemp = NULL;
  344.         short        maxWidth = 0;
  345.         StringPtr    STraverse = NULL;
  346.         short        lineCount = 1;
  347.         short        lastReturn=1, thisReturn= 1;
  348.         
  349.         if (this->Root)
  350.           ((CPPVisualTree *)this->Root)->Prepare(this);
  351.         
  352.         GetFontInfo (&fInfo);
  353.         
  354.         if (this->nodeData)
  355.           STemp = ((CPPString *)this->nodeData)->GetString(TRUE);
  356.         if (STemp)
  357.           {
  358.               lineCount = PStrReplace(STemp, ' ', '\r') + 1;
  359.             if (lineCount == 1)
  360.               this->nodeSize.h = StringWidth(STemp) + hMargin * 2;
  361.             else
  362.               {
  363.                   // find the maximum length string
  364.                   while (thisReturn <= *STemp)
  365.                     {
  366.                       thisReturn = PStrPos(STemp, '\r', lastReturn);
  367.                       if (!thisReturn)
  368.                         thisReturn = *STemp + 1;
  369.                         if (STraverse)
  370.                           DisposePtr((Ptr)STraverse);
  371.                         STraverse = Data2PString(STemp+lastReturn, thisReturn - lastReturn);
  372.                         maxWidth = Max (maxWidth, StringWidth(STraverse));
  373.                         lastReturn = thisReturn + 1;
  374.                     }
  375.                   this->nodeSize.h = maxWidth + hMargin * 2;
  376.               }
  377.             DisposePtr((Ptr)STemp);
  378.           }
  379.         else
  380.           this->nodeSize.h = 10;
  381.         
  382.         this->nodeSize.v = (fInfo.ascent + fInfo.descent + fInfo.leading) * lineCount + vMargin;
  383.         
  384.         if (this->Root)
  385.           ((CPPVisualTree *)this->Root)->Restore(this);
  386.  
  387.         /*•*/        
  388.  
  389.     }
  390.  
  391. /*-----------------------------------------------------------------*/
  392.  
  393.     void     CPPVisualTreeNode::DoCalcFamilySize(void)
  394.     /* Calculate how large the node's children, and the entire node */
  395.     /* are, storing the values in childSize and familySize */
  396.     {
  397.         CPPVisualTreeNode *theChild;
  398.         orientStyle    Orient = GetOrientation();
  399.         
  400.         if (!this->needsResize) return;
  401.         this->needsResize = FALSE;
  402.         this->CalcNodeSize();            // figure out node's size first
  403.         
  404.         
  405.         SetPt (&this->childSize, 0, 0);    // assume we have no children
  406.         
  407.         // iterate over all the children of this node, accumulating
  408.         // their height and width
  409.         for (long i = 1; i<= this->numItems; i++)
  410.           {
  411.               theChild = (CPPVisualTreeNode *)NthChild(i);
  412.               theChild->CalcFamilySize();
  413.               if ((Orient == kTopDown) || (Orient == kBottomUp))
  414.                 {
  415.                     childSize.h += theChild->familySize.h;
  416.                     childSize.v = Max (theChild->familySize.v, childSize.v);
  417.                 }
  418.               else
  419.                 {
  420.                     childSize.v += theChild->familySize.v;
  421.                     childSize.h = Max (theChild->familySize.h, childSize.h);
  422.                 }
  423.           }
  424.         
  425.         // Set the size of the node - a combination of the node size,
  426.         // child size, and branch length;
  427.         if ((Orient == kTopDown) || (Orient == kBottomUp))
  428.           {
  429.               familySize.h = Max(childSize.h, nodeSize.h);
  430.               familySize.v = nodeSize.v;
  431.               if (childSize.v > 0)
  432.                 familySize.v += childSize.v + GetBranchLength();
  433.           }
  434.         else
  435.           {
  436.               familySize.v = Max(childSize.v, nodeSize.v);
  437.               familySize.h = nodeSize.h;
  438.               if (childSize.h > 0)
  439.                 familySize.h += childSize.h + GetBranchLength();
  440.           }
  441.           
  442.     }
  443.  
  444. /*-----------------------------------------------------------------*/
  445. /*---------------------- START NORMAL TREE ------------------------*/
  446. /*-----------------------------------------------------------------*/
  447.     
  448.     void     CPPVisualTreeNode::DoCalcFamilyBounds(Point TopLeftCorner)
  449.     /* This routine uses the orientation and justification of the */
  450.     /* tree to determine where every node should be in relation to */
  451.     /* all the others. */
  452.     /* NOTE:  this routine assumes that the child/node/familySize */
  453.     /* variables are set correctly, so if any node has changed size, */
  454.     /* you should call CalcFamilySize before this routine */
  455.     {
  456.         Rect        OldNodeRect = this->nodeRect,
  457.                     OldGChildRect = this->gChildRect;
  458.                             
  459.         Rect        newNodeRect = {0, 0, 0, 0},
  460.                     newChildRect = {0, 0, 0, 0},
  461.                     newFamilyRect = {0, 0, 0, 0},
  462.                     newGChildRect = {0, 0, 0, 0};
  463.                     
  464.         Boolean        eraseKidJoins = FALSE;
  465.         orientStyle    Orient    =    GetOrientation();
  466.         short        betweenNodeMargin = GetNodeMargin();
  467.         justStyle    Just    =    GetJustification();
  468.         short        Branch    =    GetBranchLength();
  469.         Point        TL        =    TopLeftCorner,
  470.                     L, R, T, B;
  471.         CPPVisualTreeNode    *theChild = NULL;
  472.         long        i        =    0;
  473.         Rect        emptyRect = {0,0,0,0};
  474.         short        Sign    =     1, Temp;
  475.         
  476.         if (this->numItems)
  477.           {    // this node is non terminal; its family's size depends on
  478.               // the size of its children
  479.               switch (Orient) {
  480.               
  481.                   case kRight2Left :
  482.                       Sign = -1;
  483.                   case kLeft2Right :
  484.                     // The trick here is to step through each child, assimilating its 
  485.                     // boundsrect into the family boundsrect. 
  486.                     // Each time you come out of a calculate call, move TL down to the 
  487.                     // most recently calculated bottommost position; this will become the 
  488.                     // top left corner of the next child's boundsrect 
  489.                      TL.h += (this->nodeSize.h + Branch) * Sign;
  490.                                     
  491.                       // determine the node's rectangle
  492.                       if (Sign > 0)
  493.                         SetRect(&newNodeRect, TopLeftCorner.h, TopLeftCorner.v, 
  494.                               TopLeftCorner.h + this->nodeSize.h, 
  495.                               TopLeftCorner.v + this->nodeSize.v);
  496.                       else    // TopLeft is actually TopRight
  497.                         SetRect (&newNodeRect, TopLeftCorner.h - this->nodeSize.h,
  498.                                     TopLeftCorner.v, TopLeftCorner.h,
  499.                                     TopLeftCorner.v + this->nodeSize.v);
  500.                 
  501.                 // if the children are not as tall as the parent, we have to pad
  502.                 // TL.v so that they will be justified correctly
  503.                 if (this->childSize.v < this->nodeSize.v)
  504.                       {    
  505.                           switch (Just) {
  506.                             case kJustBottom   : 
  507.                                 TL.v += (this->nodeSize.v - this->childSize.v); 
  508.                                 break;
  509.                             case kJustCenter     : 
  510.                                 TL.v += ceil ((this->nodeSize.v - this->childSize.v) / 2.0); 
  511.                                 break;
  512.                           }    // switch
  513.                   }    // if
  514.                 
  515.                 // calculate bounds for the children, then assimilate them
  516.                   for (i=1;i<=this->numItems;i++)
  517.                     {
  518.                         theChild = (CPPVisualTreeNode *)NthChild(i);
  519.                         if (theChild)
  520.                           {
  521.                               theChild->DoCalcFamilyBounds(TL);
  522.                             TL.v += (theChild->familyRect.bottom - theChild->familyRect.top);
  523.                             if (i != this->numItems)
  524.                               TL.v += betweenNodeMargin;
  525.                             if (i != 1)
  526.                               {
  527.                                 UnionRect(&newChildRect, &theChild->familyRect, &newChildRect);
  528.                                 UnionRect(&newGChildRect, &theChild->nodeRect, &newGChildRect);
  529.                               }
  530.                             else
  531.                               {
  532.                                 newChildRect = theChild->familyRect;
  533.                                 newGChildRect = theChild->nodeRect;
  534.                               }
  535.                           }
  536.                     }
  537.                     
  538.                 // construct the family's rectangle
  539.                       if (EqualRect(&newChildRect, &emptyRect))
  540.                         newFamilyRect = newNodeRect;
  541.                       else
  542.                         UnionRect (&newChildRect, &newNodeRect, &newFamilyRect);
  543.  
  544.                       // now use the justification to position the node's rectangle
  545.                       switch (Just) {
  546.                         case kJustBottom : 
  547.                             OffsetRect(&newNodeRect, 0, (newFamilyRect.bottom - 
  548.                                         newNodeRect.bottom));    
  549.                                 break;
  550.                         case kJustCenter : 
  551.                                 if (this->numItems > 1)
  552.                                   {
  553.                                       ((CPPVisualTreeNode *)NthChild(1))->GetAnchorPoints (&L, &R, &T, &B);
  554.                             Temp = R.v;
  555.                                       ((CPPVisualTreeNode *)NthChild(this->numItems))->GetAnchorPoints (&L, &R, &T, &B);
  556.                                       OffsetRect (&newNodeRect, 0, (Temp + ((R.v - Temp) / 2)) - 
  557.                                                   (newNodeRect.top + ((newNodeRect.bottom - newNodeRect.top) / 2)));
  558.                                   }
  559.                                 else
  560.                                   OffsetRect(&newNodeRect, 0, (newFamilyRect.bottom - 
  561.                                              newNodeRect.bottom) / 2);
  562.                                 break;
  563.                       }    /*switch Distribution */
  564.  
  565.                 break;
  566.                 
  567.                 
  568.                   case kBottomUp :
  569.                       Sign = -1;
  570.                   case kTopDown :
  571.                     // The trick here is to step through each child, assimilating its 
  572.                     // boundsrect into the family boundsrect. 
  573.                     // Each time you come out of a calculate call, move TL down to the 
  574.                     // most recently calculated rightmost position; this will become the 
  575.                     // top left corner of the next child's boundsrect 
  576.                      TL.v += (this->nodeSize.v + Branch) * Sign;
  577.                   
  578.                       // determine the node's rectangle
  579.                       if (Sign > 0)
  580.                         SetRect(&newNodeRect, TopLeftCorner.h, TopLeftCorner.v, 
  581.                               TopLeftCorner.h + this->nodeSize.h, 
  582.                               TopLeftCorner.v + this->nodeSize.v);
  583.                       else    // TopLeft is actually BottomLeft
  584.                         SetRect(&newNodeRect, TopLeftCorner.h,
  585.                                   TopLeftCorner.v - this->nodeSize.v,
  586.                                   TopLeftCorner.h + this->nodeSize.h,
  587.                                   TopLeftCorner.v);
  588.                 
  589.                 // if the children are not as wide as the parent, we have to pad
  590.                 // TL.h so that they will be justified correctly
  591.                 if (this->childSize.h < this->nodeSize.h)
  592.                       {    
  593.                           switch (Just) {
  594.                             case kJustRight   : 
  595.                                 TL.h += (this->nodeSize.h - this->childSize.h); 
  596.                                 break;
  597.                             case kJustCenter     : 
  598.                                 TL.h += ceil((this->nodeSize.h - this->childSize.h) / 2.0); 
  599.                                 break;
  600.                           }    // switch
  601.                   }    // if
  602.                 
  603.                 // calculate bounds for the children, then assimilate them
  604.                   for (i=1;i<=this->numItems;i++)
  605.                     {
  606.                         theChild = (CPPVisualTreeNode *)NthChild(i);
  607.                         if (theChild)
  608.                           {
  609.                               theChild->DoCalcFamilyBounds(TL);
  610.                             TL.h += (theChild->familyRect.right - theChild->familyRect.left);
  611.                             if (i != this->numItems)
  612.                               TL.h += betweenNodeMargin;
  613.                             if (i != 1)
  614.                               {
  615.                                 UnionRect(&newChildRect, &theChild->familyRect, &newChildRect);
  616.                                 UnionRect(&newGChildRect, &theChild->nodeRect, &newGChildRect);
  617.                               }
  618.                             else
  619.                               {
  620.                                 newChildRect = theChild->familyRect;
  621.                                 newGChildRect = theChild->nodeRect;
  622.                               }
  623.                           }
  624.                     }
  625.                     
  626.                    // construct the family's rectangle
  627.                      if (EqualRect(&newChildRect, &emptyRect))
  628.                         newFamilyRect = newNodeRect;
  629.                       else
  630.                         UnionRect (&newChildRect, &newNodeRect, &newFamilyRect);
  631.  
  632.                     // now use the justification to position the family's rectangle
  633.                       switch (Just) {
  634.                         case kJustRight : 
  635.                             OffsetRect(&newNodeRect, (newFamilyRect.right - 
  636.                                         newNodeRect.right), 0);    
  637.                                 break;
  638.                         case kJustCenter : 
  639.                                 if (this->numItems > 1)
  640.                                   {
  641.                                       ((CPPVisualTreeNode *)NthChild(1))->GetAnchorPoints (&L, &R, &T, &B);
  642.                             Temp = T.h;
  643.                                       ((CPPVisualTreeNode *)NthChild(this->numItems))->GetAnchorPoints (&L, &R, &T, &B);
  644.                                       OffsetRect (&newNodeRect, (Temp + ((T.h - Temp) / 2)) - 
  645.                                                   (newNodeRect.left + ((newNodeRect.right - newNodeRect.left) / 2)), 0);
  646.                                   }
  647.                                 else
  648.                                   {
  649.                                       ((CPPVisualTreeNode *)NthChild(1))->GetAnchorPoints (&L, &R, &T, &B);
  650.                                     OffsetRect(&newNodeRect, T.h - (newNodeRect.left + ((newNodeRect.right - newNodeRect.left) / 2)), 0);
  651.                               }
  652.                                 break;
  653.                       }    /*switch Distribution */
  654.     
  655.                 break;
  656.  
  657.               }
  658.           }
  659.         else
  660.           {    // terminal node; family size is based on nodeSize alone
  661.               switch (Orient) {
  662.                 case kTopDown :        // TopLeft is TopLeft
  663.                 case kLeft2Right :
  664.                     SetRect (&newNodeRect, TopLeftCorner.h, TopLeftCorner.v,
  665.                              TopLeftCorner.h + this->nodeSize.h,
  666.                            TopLeftCorner.v + this->nodeSize.v);
  667.                     break;
  668.                 case kBottomUp:        // TopLeft is BottomLeft
  669.                     SetRect (&newNodeRect, TopLeftCorner.h, 
  670.                              TopLeftCorner.v - this->nodeSize.v,
  671.                              TopLeftCorner.h + this->nodeSize.h,
  672.                              TopLeftCorner.v);
  673.                     break;
  674.                 case kRight2Left :    // TopLeft is TopRight
  675.                     SetRect (&newNodeRect, TopLeftCorner.h - this->nodeSize.h,
  676.                              TopLeftCorner.v,
  677.                              TopLeftCorner.h,
  678.                              TopLeftCorner.v + this->nodeSize.v);
  679.                     break;
  680.               }
  681.               newFamilyRect = newNodeRect;
  682.           }
  683.         
  684.  #include <TreeNodeConds.cp>
  685.               
  686.          this->nodeRect = newNodeRect;
  687.          this->childRect = newChildRect;
  688.          this->familyRect = newFamilyRect;
  689.             this->gChildRect = newGChildRect;
  690.             
  691.         this->notYetPlaced = FALSE;
  692.     }
  693.  
  694. /*-----------------------------------------------------------------*/
  695.  
  696.     long    CPPVisualTreeNode::NumSelectedNodes (CPPVisualTreeNode *theNode)
  697.     /* return the number of nodes in the family which are selected */
  698.     /* NOTE:  The family head is included in the count */
  699.     {
  700.         long    count = 0;
  701.         
  702.         if (theNode)
  703.           ApplyToFamily (theNode, CountSelected, (long)(&count));
  704.         
  705.         return count;
  706.     }
  707.  
  708. /*-----------------------------------------------------------------*/
  709.  
  710.     void    CPPVisualTreeNode::SetSelect (short selectType, 
  711.                                           Boolean selectFamily,
  712.                                           Boolean doRedraw)
  713.     /* Set the selection state of the node according to the passed */
  714.     /* in value.  Redraw the node if asked, and pass the selection */
  715.     /* state down to the node's children if asked */
  716.     {
  717.         short    oldValue = this->isSelected;
  718.         
  719.         switch (selectType) {
  720.         
  721.             case kSetSelect :
  722.                 this->isSelected = TRUE;
  723.                 if (!oldValue && doRedraw)
  724.                   this->DrawNode(FALSE, TRUE);    // redraw if requested
  725.                 break;
  726.                 
  727.             case kClearSelect :
  728.                 this->isSelected = FALSE;
  729.                 if (oldValue && doRedraw)
  730.                   this->DrawNode(FALSE, TRUE);    // redraw if requested
  731.                 break;
  732.                 
  733.             case kToggleSelect :
  734.                 this->isSelected = !oldValue;
  735.                 if (doRedraw)        
  736.                   this->DrawNode(FALSE, TRUE);    // redraw if requested
  737.                 break;
  738.         }
  739.         
  740.         // if asked, pass it on to the node's children
  741.         if (selectFamily)
  742.           for (long i = 1; i <= this->numItems; i++)
  743.             ((CPPVisualTreeNode *)NthChild(i))->
  744.                 SetSelect(selectType, selectFamily, doRedraw);
  745.     }
  746.  
  747. /*-----------------------------------------------------------------*/
  748.  
  749.     void    CPPVisualTreeNode::GetAnchorPoints (Point *Left, Point *Right, 
  750.                                                 Point *Top, Point *Bottom)
  751.     /* Return the points on the left, right, top, and bottom where the */
  752.     /* joins should start/end */
  753.     {
  754.         short    halfWidth = (nodeRect.right - nodeRect.left) / 2;
  755.         short    halfHeight = (nodeRect.bottom - nodeRect.top) / 2;
  756.         
  757.         SetPt (Left, nodeRect.left-1, nodeRect.top + halfHeight);
  758.         SetPt (Right, nodeRect.right, nodeRect.top + halfHeight);
  759.         SetPt (Top, nodeRect.left + halfWidth, nodeRect.top-1);
  760.         SetPt (Bottom, nodeRect.left + halfWidth, nodeRect.bottom);
  761.     }
  762.         
  763. /*-----------------------------------------------------------------*/
  764.  
  765.     void    CPPVisualTreeNode::ReceiveMessage (CPPGossipMonger *toldBy, 
  766.                                     short reason, void* info)
  767.     {
  768.         Point    oldTopLeft = {this->familyRect.top, this->familyRect.left};
  769.         Rect    oldFamilyRect = this->familyRect;
  770.         Point    OldFamilySize = this->familySize,
  771.                 OldChildSize = this->childSize, 
  772.                 OldNodeSize = this->nodeSize;
  773.         Boolean    childChanged, familyChanged;
  774.         RgnHandle    Rgn1, Rgn2;
  775.         static    short    timesCalled = 0;    // tracks recursion
  776.         
  777.         switch (reason) {
  778.             case kEraseFamily    :
  779.                 EraseNode ((Boolean)info);
  780.                 break;
  781.                 
  782.             case kDrawFamily    :
  783.                 DrawNode ((Boolean)info, TRUE);
  784.                 break;
  785.                 
  786.             case kResizeFamily    :
  787.                 ResizeFamily (TRUE);
  788.                 if (this->Root)
  789.                   ((CPPVisualTree *)this->Root)->AdjustTree();
  790.                 break;
  791.                 
  792.             case kMoveFamily    :
  793.                 MoveFamily ((Point *)info);
  794.                 if (this->Root)
  795.                   ((CPPVisualTree *)this->Root)->AdjustTree();
  796.                 break;
  797.                 
  798.             case kPrepareRemove :
  799.             case kPrepareDelete :
  800.                 ((CPPVisualTreeNode *)info)->EraseNode(TRUE);
  801.                 ApplyToFamily((CPPVisualTreeNode *)info, SetNotYetPlaced, TRUE);
  802.                 break;
  803.                 
  804.             case kChildRemoved    :
  805.             case kChildDeleted    :
  806.             case kNodeAdded    :    // the parent of the added node receives this message first
  807.             case kNodeChangedData :
  808.                 timesCalled++;
  809.                 
  810.                 if (timesCalled == 1)
  811.                   if (this->Root)
  812.                     ((CPPVisualTree *)this->Root)->Prepare((CPPObject *)1313L);
  813.                     
  814.                 this->needsResize = TRUE;
  815.                 this->needsMove = TRUE;
  816.                 CalcFamilySize();                // figure out how big everyone is                
  817.                 CalcFamilyBounds (oldTopLeft);    // figure out where everybody goes
  818.                 
  819.                 if (info)
  820.                   this->needsDraw = TRUE;
  821.  
  822.                 if (this->Parent && !EqualRect(&oldFamilyRect, &this->familyRect))
  823.                   TellParent (reason, NULL);
  824.  
  825.                 if (this->needsDraw)
  826.                   {
  827.                       this->DrawNode(TRUE, FALSE);
  828.                       if (this->Root)
  829.                       ((CPPVisualTree *)this->Root)->DrawAllJoins(TRUE);
  830.                       if ((OldFamilySize.h > this->familySize.h) || (OldFamilySize.v > this->familySize.v))
  831.                         {
  832.                             Rgn1 = NewRgn();
  833.                             Rgn2 = NewRgn();
  834.                             RectRgn(Rgn1, &oldFamilyRect);
  835.                             RectRgn(Rgn2, &this->familyRect);
  836.                             XorRgn (Rgn1, Rgn2, Rgn1);
  837.                             EraseRgn (Rgn1);
  838.                             DisposeRgn(Rgn1);
  839.                             DisposeRgn(Rgn2);
  840.                         }
  841.                       if (this->Root)
  842.                       ((CPPVisualTree *)this->Root)->AdjustTree();
  843.                   }
  844.                   
  845.                 timesCalled--;
  846.                 if (timesCalled == 0)
  847.                   if (this->Root)
  848.                     ((CPPVisualTree *)this->Root)->Restore((CPPObject *)1313L);
  849.                 break;
  850.                 
  851.             default:
  852.                 CPPTreeNode::ReceiveMessage (toldBy, reason, info);
  853.                 break;
  854.         }
  855.             
  856.     }
  857.  
  858. /*-----------------------------------------------------------------*/
  859.  
  860.     void    CPPVisualTreeNode::GetFamilyBounds (Rect *bounds)
  861.     /* return the size of the node & its children */
  862.     {
  863.         *bounds = this->familyRect;
  864.     }
  865.  
  866. /*-----------------------------------------------------------------*/
  867.  
  868.     orientStyle    CPPVisualTreeNode::GetOrientation (void)
  869.     /* return the orientation used by the tree which this node belongs */
  870.     /* to or topDown as a default */
  871.     {
  872.         return (this->Root) ? 
  873.             ((CPPVisualTree *)Root)->GetOrientation() : kTopDown;
  874.     }
  875.     
  876. /*-----------------------------------------------------------------*/
  877.  
  878.     short    CPPVisualTreeNode::GetNodeMargin (void)
  879.     /* return the orientation used by the tree which this node belongs */
  880.     /* to or topDown as a default */
  881.     {
  882.         return (this->Root) ? 
  883.             ((CPPVisualTree *)Root)->GetNodeMargin() : 2;
  884.     }
  885.     
  886. /*-----------------------------------------------------------------*/
  887.  
  888.     justStyle    CPPVisualTreeNode::GetJustification (void)
  889.     /* return the justification used by the tree which this node belongs */
  890.     /* to or kJustCenter as a default */
  891.     {
  892.         return (this->Root) ? 
  893.             ((CPPVisualTree *)Root)->GetJustification() : kJustCenter;
  894.     }
  895.     
  896. /*-----------------------------------------------------------------*/
  897.  
  898.     joinTypes    CPPVisualTreeNode::GetJoinType (void)
  899.     /* return the join type used by the tree which this node belongs */
  900.     /* to or kRightAngle as a default */
  901.     {
  902.         return (this->Root) ?  
  903.             ((CPPVisualTree *)Root)->GetJoinType() : kRightAngle;
  904.     }
  905.     
  906. /*-----------------------------------------------------------------*/
  907.  
  908.     short    CPPVisualTreeNode::GetBranchLength (void)
  909.     /* return the branch length used by the tree which this node belongs */
  910.     /* to or 25 as a default */
  911.     {
  912.         return (this->Root) ?  
  913.             ((CPPVisualTree *)Root)->GetBranchLength() : 25;
  914.     }
  915.     
  916. /*-----------------------------------------------------------------*/
  917.  
  918.     CPPVisualTreeNode *CPPVisualTreeNode::PointInNode (Point clickPt)
  919.     /* If the point is in this node or any of its children, return */
  920.     /* the node which was clicked in, otherwise, return FALSE */
  921.     {
  922.         CPPVisualTreeNode *childNode, *returnNode = NULL;
  923.         
  924.         if (PtInRect (clickPt, &nodeRect))
  925.           return this;
  926.         else
  927.           if (PtInRect (clickPt, &childRect))
  928.             {
  929.               for (long i = 1; i <= this->numItems; i++)
  930.                 {
  931.                   if ((childNode = (CPPVisualTreeNode *)NthChild (i)) != NULL)
  932.                     if ((returnNode = childNode->PointInNode(clickPt)) != NULL)
  933.                       break;
  934.                 }
  935.               return returnNode;
  936.             }
  937.           else
  938.             return NULL;
  939.     }
  940.  
  941. /*-----------------------------------------------------------------*/
  942.  
  943.     void    CPPVisualTreeNode::DoOnFamilyInRect (Rect *theRect, 
  944.                                           Boolean fullyInside, 
  945.                                           ApplyProc theProc, long param)
  946.     /* If the node is either fully or partially inside the passed rect */
  947.     /* call theProc to perform some operation on it */
  948.     {
  949.         CPPVisualTreeNode    *childNode;
  950.         Rect                tempRect;
  951.         
  952.         // check to see if the rectangle intersects our own node
  953.         if (SectRect (theRect, &this->nodeRect, &tempRect))
  954.           {
  955.               if (!fullyInside || (fullyInside && EqualRect (&tempRect, &this->nodeRect)))
  956.                 if (theProc)
  957.                   (*theProc)(this, param);
  958.           }
  959.         
  960.         // if it intersects our children, apply SelectInRect to each child
  961.         if (SectRect (theRect, &this->childRect, &tempRect))
  962.           for (long i = 1; i <= this->numItems; i++)
  963.             {
  964.               if ((childNode = (CPPVisualTreeNode *)NthChild (i)) != NULL)
  965.                 childNode->DoOnFamilyInRect (theRect, fullyInside, theProc, param);
  966.             }
  967.     }
  968.  
  969. /*-----------------------------------------------------------------*/
  970.  
  971.     void    CPPVisualTreeNode::DoDoubleClick (short modifiers)
  972.     /* Special action to perform when a node is double-clicked */
  973.     /* SUBCLASS SHOULD OVERRIDE */
  974.     {
  975.     
  976.     }
  977.     
  978. /*-----------------------------------------------------------------*/
  979.  
  980.     void    CPPVisualTreeNode::DoSingleClick (short modifiers)
  981.     /* Special action to perform when a node is clicked */
  982.     /* SUBCLASS SHOULD OVERRIDE */
  983.     {
  984.     
  985.     }
  986.  
  987.